classdef AffineForm
    %UNTITLED3 Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
		mean
		values
		variables
    end
	
	properties ( Constant )
        var_index = 0;
    end
    
    methods
        
       function affineForm=AffineForm(value)
            if nargin==0 % Used when objects are loaded from disk
              affineForm.mean=0;
			  affineForm.values=[];
			  affineForm.variables=[];
              return;
            end
			 val_class=class(value);
			 if strcmp(val_class,'AffineForm')
                affineForm=value;
                return
             end
			 if strcmp(val_class,'Interval')
                 affineForm.mean=(value.imax+value.imin)/2;
				 affineForm.values=(value.imax-value.imin)/2;
				 affineForm.variables=AffineForm.getNewVarName();
                 
                return
             end
             if strcmp(val_class,'double')
				affineForm.mean=value;
				affineForm.values=[];
				affineForm.variables=[];
                return
             end
             affineForm.mean=0;
			 affineForm.values=[];
			 affineForm.variables=[];
        end
		
		function r=merge(X,Y,funct)
            if  isempty(X)
                X=AffineForm(0);
            end
            if  isempty(Y)
                Y=AffineForm(0);
            end
			X=AffineForm(X);Y=AffineForm(Y);
			xSize=length(X.variables);
			ySize=length(Y.variables);
             if(xSize~=length(X.values))
                        if isempty(X.values)
                            X.values=0;
                        end
             end
             if(ySize~=length(Y.values))
                        if isempty(Y.values)
                            Y.values=0;
                        end
             end
			valSize=xSize+ySize;
			r=AffineForm(funct(X.mean,Y.mean));
			xCount=1;
			yCount=1;
			for i=1:valSize
				if(xCount>xSize)
						r.variables=[r.variables, Y.variables(yCount:end)];
						r.values=[r.values, Y.values(yCount:end)];					
						return
				end
				if(yCount>ySize)
						r.variables=[r.variables, X.variables(xCount:end)];
						r.values=[r.values, X.values(xCount:end)];					
						return
				end
				
				if(X.variables(xCount)<Y.variables(yCount))
					r.variables(i)=X.variables(xCount);
                   
					r.values(i)=X.values(xCount);
					xCount=xCount+1;
					continue
				end
				if(Y.variables(yCount)<X.variables(xCount))
                   
					r.variables(i)=Y.variables(yCount);
					r.values(i)=Y.values(yCount);
					yCount=yCount+1;
					continue
				end
				
				if(Y.variables(yCount)==X.variables(xCount))
					r.variables(i)=X.variables(xCount);
					r.values(i)=funct(X.values(xCount),Y.values(yCount));
					xCount=xCount+1;
					yCount=yCount+1;
					continue
				end
			end
		end
		
        function r=plus(X,Y)  
			r=merge(X,Y,@plus);
        end
        function r=minus(X,Y) 
			r=merge(X,Y,@minus);
        end
        function r=mtimes(X,Y)
			X=AffineForm(X);Y=AffineForm(Y);
            if  isempty(X)
                X=AffineForm(0);
            end
            if  isempty(Y)
                Y=AffineForm(0);
            end
			xSize=length(X.variables);
			ySize=length(Y.variables);
			valSize=xSize+ySize;
			r=AffineForm(X.mean*Y.mean);
			
			newVariables=[];
			newValues=[];
			
			for i=1:xSize
				for j=1:ySize
					newVariables(i+j-1)=AffineForm.getNewVarName();
					newValues(i+j-1)=X.values(i)*Y.values(j);
				end
			end
			tX=X;
            tX.values=X.values.*Y.mean;
            tY=Y;
            tY.values=Y.values.*X.mean;
            tZ=tX+tY;
			r.variables=[tZ.variables, newVariables];
			r.values   =[tZ.values, newValues];
			
			r=AffineForm.sortVariables(r);
        end
        function r=mrdivide(X,Y)
			X=AffineForm(X);Y=AffineForm(Y);
			r=X*inv(Y);
        end
		
		function affine=inv(X)
			X=AffineForm(X);
			xInterval=X.convertToInterval();
			a=xInterval.imin;
			b=xInterval.imax;
			
			t1=abs(a);
			t2=abs(b);
			
			a=min(t1, t2);
			b=max(t1, t2);
			
			alpha=-1/(b*b);
            l = (1/a)-alpha*a;
            h = 2/b;
            t1=abs(l);
			t2=abs(h);
			l=min(t1, t2);
			h=max(t1, t2);
			radius=(h-l)/2;
            dzeta=(l+h)/2;
			affine=AffineForm.addApproximationError(X,alpha,dzeta,radius);
		end
		
        
        function r=max(X,Y)
		  X=AffineForm(X);Y=AffineForm(Y);
          iX=X.convertToInterval('first'); 
 		  iY=Y.convertToInterval('second');
		  iMax=max(iX,iY);
		  if strcmp(iMax.name,iX.name)
			r=X;
			return
		  end
		  if strcmp(iMax.name,iY.name)
			r=Y;
			return
		  end
		  r=AffineForm(iMax);
        end
		
        function r=min(X,Y)
		  X=AffineForm(X);Y=AffineForm(Y);
          iX=X.convertToInterval('first'); 
 		  iY=Y.convertToInterval('second');
		  iMax=min(iX,iY);
		  if strcmp(iMax.name,iX.name)
			r=X;
			return
		  end
		  if strcmp(iMax.name,iY.name)
			r=Y;
			return
		  end
		  r=AffineForm(iMax);
         end
		 
		 
		 
		 function C=lt(X,Y)
		   X=AffineForm(X);Y=AffineForm(Y);
           iX=X.convertToInterval('first'); 
 		   iY=Y.convertToInterval('second'); 
		   C=lt(iX,iY);
         end
         
         function C=gt(X,Y)
		    X=AffineForm(X);Y=AffineForm(Y);
            iX=X.convertToInterval('first'); 
 		    iY=Y.convertToInterval('second'); 
		    C=gt(iX,iY); 
         end
         
         function C=le(X,Y)
		    X=AffineForm(X);Y=AffineForm(Y);
            iX=X.convertToInterval('first'); 
 		    iY=Y.convertToInterval('second'); 
		    C=le(iX,iY); 
         end
         
         function C=ge(X,Y)
		    X=AffineForm(X);Y=AffineForm(Y);
            iX=X.convertToInterval('first'); 
 		    iY=Y.convertToInterval('second'); 
		    C=ge(iX,iY);
         end 
		 
         function interval=convertToInterval(obj,name)
             if nargin==1 % Used when objects are loaded from disk
              name='affine';
            end 
			sumArr=sum(obj.values);
			interval=Interval(obj.mean-sumArr,obj.mean+sumArr, name);
         end
        
		
		function aff=prepareAffine(value)
			 val_class=class(value);
             if strcmp(val_class,'Interval')
                aff=AffineForm(value);
                return
             end
             if strcmp(val_class,'double')
				aff=AffineForm(value);
                return
             end
		end
        
    end
	
	methods (Static)
		function var_name=getNewVarName()
            persistent p;
            if  isempty(p)
                p=0;
            end
			var_name=p;
			p=p+1;
		end
		
		function affine=sortVariables(aff)
			affine=aff;
			values=affine.values;
			variables=affine.variables;
            if(isempty(values) || isempty(variables))
                return
            end
			sorted=sortrows([values;variables]',2)';
			affine.values=sorted(1,:);
			affine.variables=sorted(2,:);
		end
		
		function aff = addApproximationError(affine, aplha, dzeta, delta)
			aff=AffineForm();
			aff.mean=aplha*affine.mean+dzeta;
			aff.values=[affine.values*aplha,delta];
			aff.variables=[affine.variables,AffineForm.getNewVarName()];
		end
	end
    
end

